/*
一、将下面异步代码使用Promise的方式改进
    setTimeout(function (){
        var a = 'hello'
        setTimeout(function (){
            var b = 'lagou'
            setTimeout(function (){
                var c = 'I ♥ U'
                console.log(a + b + c)
            }, 10)
        }, 10)
    }, 10)
*/

let promise = new Promise((resolve) => {
    var a = 'hello'
    resolve(a)
})

promise
    .then(res => {
        var b = 'lagou'
        return res + b
    })
    .then(res => {
        var c = 'I ♥ U'
        console.log(res + c)
    })

/*
二、基于以下代码完成下面的四个练习
    const fp = require('lodash/fp')
    // 数据
    // horsepower 马力, dollar_value 价格，in_stock 库存
*/
const cars = [{
        name: 'Ferrari FF',
        horsepower: 660,
        dollar_value: 700000,
        in_stock: true
    },
    {
        name: 'Spyker C12 Zagato',
        horsepower: 650,
        dollar_value: 648000,
        in_stock: false
    },
    {
        name: 'Jaguar XKR-S',
        horsepower: 550,
        dollar_value: 132000,
        in_stock: false
    },
    {
        name: 'Audi R8',
        horsepower: 525,
        dollar_value: 114200,
        in_stock: false
    },
    {
        name: 'Aston Martin One-77',
        horsepower: 750,
        dollar_value: 1850000,
        in_stock: true
    },
    {
        name: 'Pagani Huayra',
        horsepower: 700,
        dollar_value: 1300000,
        in_stock: false
    }
]

// 练习1：使用函数组合fp.flowRight()重新实现下面这个函数
// let isLastInStock = function (cars) {
//     // 获取最后一条数据
//     let last_car = fp.last(cars)
//     // 获取最后一条数据的in_stock 属性值
//     return fp.prop('in_stock' , last_car)
// }

const fp = require('lodash/fp')

const f = fp.flowRight(fp.prop('in_stock'), fp.last)

console.log('获取最后一条数据的in_stock 属性值', f(cars))


// 练习2：使用fp.flowRight()、fp.prop()和fp.first()获取第一个car的name
const f2 = fp.flowRight(fp.prop('name'), fp.first)

console.log('获取第一条数据的名字', f2(cars))

// 练习3：使用帮助函数_average重构averageDollarValue,使用函数组合的方式实现
// let _average = function (xs) {
//     return fp.reduce(fp.add, 0 , xs) / xs.length
// }

// let averageDollarValue = function (cars) {
//     let dollar_values = fp.map(function (car){
//         return car.dollar_valuef
//     }, cars)
//     return _average(dollar_value)
// }

let add = function (xs) {
    return fp.reduce(fp.add, 0, xs) / xs.length
}

let _average = fp.flowRight(add, fp.map(value => {
    return value.dollar_value
}))

console.log('练习3', _average(cars))

// 练习4：使用flowRight写一个sanitizeNames()函数，返回一个下划线连接的小写字符串
// ，把数组中的name转换为这种形式：例如：sanitizeNames(['Hello World'])=>['hello_world']
// let _underscore = fp.replace(/\W+/g,'_') // <--无须改动，并在sanitizeNames中使用它

let _underscore = fp.replace(/\W+/g, '_')
let sanitizeNames = fp.flowRight(fp.join(''), fp.map(fp.toLower), _underscore)

console.log('练习4', sanitizeNames(['Hello World']))


// 三、基于下面提供的代码，完成后续的四个练习
class Container {
    static of (value) {
        return new Container(value)
    }
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return Container.of(fn(this._value))
    }
}

class Maybe {
    static of (value) {
        return new Maybe(value)
    }

    constructor(value) {
        this._value = value
    }

    isNothing() {
        return this._value === null || this._value === undefined
    }

    map(fn) {
        return this.isNothing() ? this : Maybe.of(fn(this._value))
    }
}

module.exports = {
    Maybe,
    Container
}

// 练习1：使用fp.add(x,y)和fp.map(f,x)，创建一个能让functor里的值增加的函数ex1
// const {Maybe , Container} = require('./support')
let maybe = Maybe.of([5, 6, 1])
let ex1 = (num) => {
    return maybe.map(fp.map(fp.add(num)))
}
console.log('ex1', ex1(1))

// 练习2：实现一个函数ex2，能够使用fp.first获取列表的第一个元素
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {
    return xs.map(fp.first)._value
}
console.log('ex2', ex2())

// 练习3：实现一个函数ex3，使用safeProp和fp.first找到user的名字的首字母
let safeProp = fp.curry(function (x, o) {
    return Maybe.of(o[x])
})
let user = {
    id: 2,
    name: 'Albert'
}
let ex3 = () => {
    return safeProp('name', user).map(fp.first)._value
}
console.log('ex3', ex3())

// 练习4：使用Maybe重写ex4，不要有if语句
let ex4 = function (n) {
    return Maybe.of(n)
        .map(n => {
            return parseInt(n)
        })
        ._value
}
console.log('ex4', ex4('11'))


// 四、手写实现MyPromise源码

class MyPromise {
    // 构造函数
    constructor(executor) {
        // 捕获执行器的错误
        try {
            executor(this.resolve, this.reason)
        } catch (e) {
            this.reject(e)
        }
    }
    // 定义promise的状态
    status = PENDING
    // 成功返回的值
    res = undefined
    // 失败返回的值
    reason = undefined
    // 保存成功回调的数组
    successCallback = []
    // 保存失败回调的数组
    failCallback = []

    // 成功回调
    resolve = res => {
        // 判断状态是否为pending，不是则阻止程序向下执行
        if (this.status !== PENDING) return
        // 改变状态为成功
        this.status = FULFILLED
        // 保存成功回调的值
        this.res = res
        // 判断成功回调是否存在，是则调用
        while (this.successCallback.length) this.successCallback.shift()()
    }
    // 失败回调
    reject = reason => {
        // 判断状态是否为pending，不是则阻止程序向下执行
        if (this.status !== PENDING) return
        // 改变状态为成功
        this.status = REJECTED
        // 保存成功回调的值
        this.reason = reason
        // 判断成功回调是否存在，是则调用
        while (this.failCallback.length) this.failCallback.shift()()
    }

    then(successCallback, failCallback) {
        // 判断是否存在成功回调，如果不存在就传递value
        successCallback = successCallback ? successCallback : (value => value)
        // 判断是否存在失败回调，如果不存在就传递reason
        failCallback = failCallback ? failCallback : (reason => {
            throw reason
        })

        let promise = MyPromise((resolve, reject) => {
            // 判断当前promise的状态，如果不是等待中，则执行回调函数
            if (this.status == FULFILLED) {
                /* promise作为参数传递，需要走完同步代码，这里用setTimeout包裹，
                 就能让其中的代码等待其执行完成 */
                setTimeout(() => {
                    try {
                        let success = successCallback(this.res)
                        // 判断回调函数是否属于promise对象，如果是promise对象，
                        // 根据其结果判断调用resolve还是reject
                        this.resolvePromise(promise, success, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else if (this.status == REJECTED) {
                setTimeout(() => {
                    try {
                        let fail = failCallback(this.reason)

                        this.resolvePromise(promise, fail, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)

            } else {
                // 等待状态把回调函数保存起来
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let success = successCallback(this.res)

                            this.resolvePromise(promise, success, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let fail = failCallback(this.reason)

                            this.resolvePromise(promise, fail, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })

        return promise
    }
    // 无论promise对象的回调是成功的还是失败的，finally方法都会被执行一次
    // 在finally方法后面可以链式调用then方法拿到当前promise对象的最终返回结果
    finally(callback) {
        // 得到当前promise对象的状态
        return this.then(() => {
            // 不管传的回调是同步的还是异步的，都返回一个promise并执行
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            // 失败同理
            return MyPromise.resolve(callback()).then(() => {
                throw reason
            })
        })
    }
    // 捕获失败的回调
    catch (failCallback) {
        // 返回失败的回调
        return this.then(undefined, failCallback)
    }
    // all方法是一个静态方法
    static all(arr) {
        // 保存回调结果
        let result = []
        // 记录执行次数
        let index = 0
        return MyPromise((resolve, reject) => {
            // for循环中会有异步代码，需要判断所有代码执行完成后才调用resolve
            // 保存回调结果
            function addData(key, value) {
                result[key] = value
                // 执行次数加一
                index++
                // 如果执行次数等于数组的长度，说明for循环中的所有代码执行完成
                if (index == arr.length) {
                    resolve(result)
                }
            }
            // 遍历promise
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                // 判断是否是promise对象
                if (current instanceof MyPromise) {
                    current.then(val => {
                        // 保存成功回调的value
                        addData(i, val)
                    }, reason => {
                        // 只要有一次失败就抛出失败回调
                        reject(reason)
                    })
                } else {
                    // 保存普通值
                    addData(i, current)
                }
            }
        })
    }
    // resolve静态方法
    static resolve(value) {
        // 如果传递的是一个promise对象，原样返回该promise对象
        if (value instanceof MyPromise) return value
        // 如果传递的普通值，则用一个promise对象包裹
        return new MyPromise(resolve => resolve(value))
    }

    // 判断回调函数的参数是否为promise对象
    resolvePromise(promise, callback, resolve, reject) {
        // 如果回调函数传的是同一个promise对象，则返回reject
        if (promise === callback) {
            return reject(new TypeError('#<Promise>'))
        }

        if (callback instanceof MyPromise) {
            // promise对象
            callback.then(resolve, reject)
        } else {
            // 普通值
            resolve(callback)
        }
    }
}